try:
	from xml.etree import ElementTree as ET
except ImportError:
	from elementtree import ElementTree as ET
import re

__all__ = ["ParseError", "parse_description"]

class ParseError(Exception):
	"Parse Error"

osns = "{http://a9.com/-/spec/opensearch/1.1/}"
nsre = re.compile("^\{(?P<ns>[^\}]+)\}[a-zA-Z][a-zA-Z0-9_]+$")
termre = re.compile("^(?P<key>[a-zA-Z][a-zA-Z0-9_]+)="
		    "\{" +
		    "((?P<ns>[a-zA-Z][a-zA-Z0-9_]+):){0,1}" +
		    "(?P<tag>[a-zA-Z][a-zA-Z0-9_]+)" +
		    "(?P<optional>\?){0,1}" +
		    "\}$")

def parse_url(urltag):
	r = { "params" : [] }
	if "type" not in urltag.attrib or "template" not in urltag.attrib:
		raise ParseError("malformed url")
	m = nsre.match(urltag.tag)
	if not m:
		raise ParseError("no namespace %s" % urltag)
	ns = m.groupdict()["ns"]
	r["type"] = urltag.attrib["type"]
	template = urltag.attrib["template"]
	base, query = template.split("?", 1)
	r["base"] = base
	for param in query.split("&"):
		m = termre.match(param)
		if not m:
			raise ParseError("malformed template near: %s" % param)
		spec = m.groupdict()
		r["params"].append(spec)
	return r

def parse_description(xml):
	"""
	>>> descr = parse_description(test_description)
	>>> descr.keys()
	['Url', 'OutputEncoding', 'ShortName', 'Description', 'InputEncoding']
	>>> descr["ShortName"]
	'Duetopia Product Search'
	>>> descr["InputEncoding"]
	['UTF-8']
	"""
	r = {}
	tree = ET.fromstring(xml)
	for required in ["ShortName", "Description"]:
		tag = tree.find(osns + required)
		if tag is None:
			raise ParseError("%s not present" % required)
		r[required] = tag.text
	for optional in ["LongName"]:
		tag = tree.find(osns + optional)
		if tag is not None:
			r[optional] = tag.text
	for optional in ["InputEncoding", "OutputEncoding"]:
		r[optional] = []
		for tag in tree.findall(osns + optional):
			r[optional].append(tag.text)
	r["Url"] = []
	for tag in tree.findall(osns + "Url"):
		r["Url"].append(parse_url(tag))
	return r
	
test_description = """\
<?xml version="1.0" encoding="utf-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:geo="http://a9.com/-/opensearch/extensions/geo/1.0/" xmlns:time="http://a9.com/-/opensearch/extensions/time/1.0/">
  <ShortName>Duetopia Product Search</ShortName>
  <Description>Search Duetopia for geospatial data</Description>
  <Url type="application/xhtml+xml" template="/duetopia/envisat/meris/?keyword={searchTerms?}&amp;title={searchTerms?}&amp;acquisitionStation={searchTerms?}&amp;processingCentre={searchTerms?}&amp;start={time:Start?}&amp;end={time:End?}&amp;geometry={geo:polygon}"/>
  <Url type="application/json" template="/duetopia/envisat/meris/json/?keyword={searchTerms?}&amp;title={searchTerms?}&amp;acquisitionStation={searchTerms?}&amp;processingCentre={searchTerms?}&amp;start={time:Start?}&amp;end={time:End?}&amp;geometry={geo:polygon}"/>
  <Url type="application/vnd.google-earth.kml+xml" template="/duetopia/envisat/meris/kml/?keyword={searchTerms?}&amp;title={searchTerms}&amp;acquisitionStation={searchTerms?}&amp;processingCentre={searchTerms?}&amp;start={time:Start?}&amp;end={time:End?}&amp;geometry={geo:polygon}"/>
  <InputEncoding>UTF-8</InputEncoding>
  <OutputEncoding>UTF-8</OutputEncoding>
</OpenSearchDescription>
"""

if __name__ == '__main__':
	import doctest
	doctest.testmod()
